#include "strman.h"
#include "lang.h"

#include "../sleipner_config.h"
#include "script/scripthelper.h"
#include "vfs/vfs.h"

#include "util/common_macros.h"		// macros
#include "util/log.h"		    // logging
#include "util/file_io.h"		    // file
#include "util/mmgr.h"		

#include <wchar.h>

using namespace lang;
using namespace script;
using namespace util;
#pragma pack(1)


extern inline unsigned short CPU_to_LE16(unsigned short x) { return (x<<8) | (x>>8);}
extern inline unsigned long CPU_to_LE32(unsigned long x) { return((x>>24) |((x>>8)&0xff00) | ((x<<8)&0xff0000) | (x<<24));}


StrMan&	StrMan::Instance()
{
	static StrMan	Inst;
	return Inst;
}

StrMan::StrMan() :
num::NamedSet<StrEntry*>(true, true)
{
	int j =0;
}

StrMan::~StrMan()
{
	int j = 0;
}

bool		StrMan::LoadLangBin(const std::string& File)
{
#ifdef _NOLOCALIZATION_
	return false;
#endif

	 std::wstring tempcmn;

	//const std::string File = "localization/English.bin";

	//_LOG(MSG_COMMENT,"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++1 " << File);

	ClearSet();
	const char* Filename = File.c_str();
	vfs::LoadedBlock		Block;
	if (!vfs::VFS::Instance().GetFile(File, Block))
	{
		_LOG(MSG_ERROR, "Unable to load Language file " << File << "!");
		return false;
	}
	// binary files are always unicode
    
	unsigned int MAGIC = 'locb';
	unsigned long	KeyID;
	Block.ReadData(&KeyID, sizeof(KeyID));
	//KeyID = KeyID;				// switch to little endian
	if (KeyID != MAGIC)		
	{
		_LOG(MSG_ERROR, "Unable to load Language file " << File << "!");
		return false;
	}
    
	/*
     Data consists of 
     StringID :		INT (StrLen)	unsigned short * StrLen
     Value	 :		INT (StrLen)	unsigned short * StrLen
     */
	unsigned int	ID_Len;
	unsigned int	Value_Len;
	unsigned char	Buffer[4];
	const int		MAX_LENGTH		= 1024;
	unsigned short	ID_Str[MAX_LENGTH];
	unsigned short	Value_Str[MAX_LENGTH];
  
    //memset(Value_Temp, 0, sizeof(Value_Temp));
    
	do
	{
		memset(ID_Str, 0, sizeof(ID_Str));
		memset(Value_Str, 0, sizeof(Value_Str));
        
		Block.ReadData(&ID_Len, sizeof(ID_Len));
		ID_Len = cpu_to_le32(ID_Len);
		if (Block.IsEmpty())
		{
			// read past EOF
			break;
		}
        
		Block.ReadData(&Buffer, sizeof(Buffer));		// get rid of trailing junk
		UTIL_CLAMP(ID_Len, 0, MAX_LENGTH-1);
		Block.ReadData(ID_Str, ID_Len* sizeof(unsigned short));
		
		Block.ReadData(&Value_Len, sizeof(Value_Len));
		Value_Len = cpu_to_le32(Value_Len);
        
		Block.ReadData(&Buffer, sizeof(Buffer));		// get rid of trailing junk
		UTIL_CLAMP(Value_Len, 0, MAX_LENGTH-1);
		Block.ReadData(Value_Str, Value_Len*sizeof(unsigned short));
        
        // the values are stored as little-endien (from Win32). Need to change that
#ifdef _BIG_ENDIAN_ARCHITECTURE
		for (int i=0; i<Value_Len; i++)
		{
			Value_Str[i] = cpu_to_le16(Value_Str[i]);
		}		for (int i=0; i<ID_Len; i++)
		{
			ID_Str[i] = cpu_to_le16(ID_Str[i]);
		}
#endif //_BIG_ENDIAN_ARCHITECTURE
        
		if (wcslen((const wchar_t*) Value_Str) > 0 && wcslen((const wchar_t*) ID_Str) > 0)
		{
			StrEntry* pEntry = new StrEntry();
			pEntry->Data = (wchar_t*)Value_Str;
            
            std::wstring ws;
            ws.assign(ID_Str,ID_Str + sizeof(ID_Str));
            
			std::string	Key = Lang::ConvertWtoA(ws);  
            
			if (ElementExists(Key))
			{
				delete pEntry;
			} else
			{
                
#if defined( _PLATFORM_IOS) || defined(_PLATFORM_ANDROID)
                
                
                /*
                for(int i = 0; i < MAX_LENGTH; i++)
                {
                    if( Value_Str[i] != 0)
                    {
                        Value_Temp[j] = Value_Str[i];
                        //Value_Temp[j+1] = Value_Str[i];
                        j++;
                    }
                    
                }
                for(int i = 0; i < MAX_LENGTH; i++)
                {
                    Value_Str[i] = Value_Temp[i];
                }
                */



                //_LOG(MSG_COMMENT,"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++3ia " << File);

				tempcmn.assign( Value_Str, Value_Str + sizeof(Value_Str) );
               // _LOG(MSG_COMMENT,"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++3ib " << File);

                wcscpy( (wchar_t*)Value_Str , tempcmn.c_str());
               // _LOG(MSG_COMMENT,"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++3ic " << File);

                pEntry->Data = (wchar_t*)Value_Str;
                //_LOG(MSG_COMMENT,"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++3j " << File);

                
#endif
             
                //std::wstring ws_value;
               // ws_value.assign(Value_Str,Value_Str + sizeof(Value_Str));
               // pEntry->Data = ws_value.c_str();
                
             	AddElement(Key, pEntry);

				// TESTING
				//_LOG(MSG_COMMENT, Key.c_str());
				//std::string Test = Lang::ConvertWtoA(pEntry->Data);
				//_LOG(MSG_COMMENT, Test.c_str());
			}
		}
	} while (!Block.IsEmpty());
    
	    
	return true;
}

bool		StrMan::LoadLangLua(const std::string& File)
{
	ClearSet();
	// load packed binary file
	LuaPlus::LuaState*	pScript = ScriptHelper::CreateLuaState();
	if (!ScriptHelper::DoFile(*pScript, File))
	{
		_LOG(MSG_ERROR, "Unable to load Language file " << File << "!");
        LuaPlus::LuaState::Destroy( pScript );
		return false;
	} else
	{
		LuaPlus::LuaObject	Str = ScriptHelper::GetObj(*pScript, "Strings");
		if (Str.IsTable())
		{
			int Count = Str.GetCount();
			int	Index = 1;
			while (Index+1 < Count)
			{
				StrEntry* pEntry = new StrEntry;
				std::string	Key = Str.GetByIndex(Index++).GetString();
				std::string	Data = Str.GetByIndex(Index++).GetString();
                /// convert the string
				pEntry->Data	= Lang::ConvertAtoW(Data);
				
				AddElement(Key, pEntry);
			}
		}
	}
	LuaPlus::LuaState::Destroy( pScript );
	return true;
}


const std::wstring& StrMan::GetStringA(const std::string& ID)
{
	static const std::wstring Invalid = L"";
	if (ID.empty()) return Invalid;
	const char* pTest = ID.c_str();
	StrEntry* pEntry = GetElement(ID);
	if (pEntry)
	{
		return pEntry->Data;
	}
	return Invalid;
}

const std::wstring& StrMan::GetStringW(const std::wstring& ID)
{
	static const std::wstring Invalid = L"";
	if (ID.empty()) return Invalid;
	return GetStringA(Lang::ConvertWtoA(ID));	
}
#pragma pack()
